#ifndef SPACECUBEREP_H_
#define SPACECUBEREP_H_

#include "precom.h"
#include "ModelStructs.h"

struct CubeElement {
	unsigned long	 m_triangleIndex;
	Mesh*			 m_mesh;

	CubeElement():m_triangleIndex(0), m_mesh(NULL) {}
	CubeElement(unsigned long a_num, Mesh* a_mesh):m_triangleIndex(a_num), m_mesh(a_mesh) {}
};

class SpaceCubeRep
{
public:
	SpaceCubeRep();
	virtual ~SpaceCubeRep();

	//==============================
	//	METHODs
	//
	bool ReInit(const Box& a_bbox, unsigned long a_numX, unsigned long a_numY, unsigned long a_numZ);
	bool Fill(const std::list<Mesh*>& a_meshes);
	bool GetCubeIndexForPoint(const float* a_point, unsigned long& a_x, unsigned long& a_y, unsigned long& a_z) const;
	bool GetCubeIndexForPoint2(const float* a_point, unsigned long& a_x, unsigned long& a_y, unsigned long& a_z) const;
	bool GetBoxForPoint(const float* a_point, Box& a_box);
	bool CompileSpace();
	void DrawCubeWithLines(unsigned long a_x, unsigned long a_y, unsigned long a_z) const;
	void DrawCollidingCubes() const;
	void GetCollideCubes(const Box& a_box, std::list<const CubeElement*>& o_collideCubes);// const;
	void Clear();

	//==============================
	//	INLINEs
	//
	inline bool GetCubeIndexForPoint(const fmath::vector3& a_point, unsigned long& a_x, unsigned long& a_y, unsigned long& a_z) const
	{
		return GetCubeIndexForPoint(&a_point.x, a_x, a_y, a_z);
	}

	inline bool ReInit(const Box& a_bbox, unsigned long a_num) 
	{ ReInit(a_bbox, a_num, a_num, a_num); }

	inline const std::list<CubeElement>& GetTriangleListFromCube(unsigned long a_x, unsigned long a_y, unsigned long a_z) const
	{ return m_cubes[a_x * (m_sizeY*m_sizeZ) + a_y * m_sizeZ + a_z]; }

	inline GLuint GetCompiledSpace() const
	{ return m_compiledSpace; }

	inline void CutIndexes(unsigned long& a_x, unsigned long& a_y, unsigned long& a_z)
	{
		if(a_x == m_sizeX) a_x = 0;
		if(a_y == m_sizeY) a_y = 0;
		if(a_z == m_sizeZ) a_z = 0;
		if(a_x > m_sizeX) a_x = m_sizeX -1;
		if(a_y > m_sizeY) a_y = m_sizeY -1;
		if(a_z > m_sizeZ) a_z = m_sizeZ -1;
	}

	inline float GetMinCubeSize()
	{
		return minf(minf(m_cubeSizeX, m_cubeSizeY), m_cubeSizeZ);
	}
	inline void GetSpaceDiv(unsigned long& o_x, unsigned long& o_y, unsigned long& o_z)
	{
		o_x = m_sizeX; o_y = m_sizeY; o_z = m_sizeZ;
	}
	inline void ClearCollidingCubesList()
	{ m_collidingCubes.clear(); }
	inline float GetTriangleSize() const { return m_triangleSize; }

protected:
	void AddTriangleToCubeAssignToPoint(unsigned long a_triangle, Mesh* a_mesh, const fmath::vector3& a_point);
	void PutTriangleInCubes(unsigned long a_triangle, Mesh* a_mesh);
	void PutTriangleInCubes2(unsigned long a_triangle, Mesh* a_mesh);
	
	inline std::list<CubeElement>& TriangleListFromCube(unsigned long a_x, unsigned long a_y, unsigned long a_z)
	{ return m_cubes[a_x * (m_sizeY*m_sizeZ) + a_y * m_sizeZ + a_z]; }
private:
	unsigned long							m_sizeX, m_sizeY, m_sizeZ;
	float									m_cubeSizeX, m_cubeSizeY, m_cubeSizeZ;
	float									m_begCubesVertex[3];
	std::vector<std::list<CubeElement>>		m_cubes;
	std::list<unsigned long>				m_collidingCubes;

	GLuint									m_compiledSpace;
	float									m_triangleSize;

	inline void UpdateTriangleSize(const fmath::vector3& v1, const fmath::vector3& v2, const fmath::vector3& v3)
	{
		float s = 0.0f;;
		s += (v2-v1).length();
		s += (v3-v1).length();
		s += (v3-v2).length();
		s /= 3.0f;
		m_triangleSize = maxf(s, m_triangleSize);
	}
};


#endif //SPACECUBEREP_H_